// ****************************************************************************
//	Copyright (c) 2011 Daniel Watman
//	http://www.keycommander.net/
// ****************************************************************************
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//	
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//	
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
// *********************************************************************
//	File: USBtest.c
//	Purpose: Test of MAX3420 evaluation board
//	Author: Daniel Watman
//	Created: 2011.02.25
//	Updated: 2011.02.28
//	Version: 0.1
//	Target: Atmel AVR XMEGA 128A1
//	Compiler: AVR-GCC
// *********************************************************************
//	Changes:
//		0.1	* Initial version
//		0.2	* Added USB enumeration code (works)
// *********************************************************************
//	To do:
//		* OUT direction
//		* Main loop doesnt run if EP3 interrupt enabled??
// *********************************************************************
//	Timer usage:
//		Timer C1	SPI transaction timing
//		Timer D0	Output compare for RC signal generation
//		Timer D1	16 ms tick for RC signal and misc timing
//		Timer F0	Input capture of RC receiver
//	
//	Pin usage:
//	PORTD:
//		3 O	Uart 0 TX
//	PORTC:
//		0 I	MAX3420 IRQ
//		4 O	SPI - SS
//		5 O	SPI - MOSI
//		6 I	SPI - MISO
//		7 O	SPI - SCK
//	
//	Interrupts:
//	High Priority
//		?
//	
//	Medium Priority
//		?
//	
//	Low Priority
//		PORTB_INT0	INT on port B0
//		TCD1_OVF	timer D1 overflow for general timing


#include <avr/io.h>
#include <stdio.h>	// for debugging
#include <avr/interrupt.h>
#include "usart.h"
#include "SPI.h"
#include "USB.h"
#include "types.h"

void init_osc(void);	// initialise oscillator
void init_interrupts(void);	// initialise interrupts and set priorities
void init_timers(void);	// initialise timers

void USB_create_key_pkt(u08 *);

volatile u08 flg;
volatile u16 tick;

extern volatile char TXbuf[50], TXbuf_len;	// for test messages

// USB variables
extern volatile u08 USB_configval;
extern volatile u08 USB_ep3stall;
extern volatile u08 USB_suspended;
extern volatile u08 buf[64];	// for decoding/creating messages.

int main (void) {

//PIN SETUP
	PORTC.DIRSET = 0xB8;	// set PORTC pins 4,5,7 to output for SPI, 3 for UART TX
	PORTC.OUTSET = 0x10;	// set SS high
	PORTB.PIN0CTRL = 0x1B;	// set pin B0 (IRQ), sense low level, pullup

	PORTD.OUTSET = 0x08;	// pin 3 to output for UART TX

	PORTH.DIRSET = 0xFF;	// LED outputs on port H
	PORTH.OUTSET = 0xFF;	// all LEDs off to start

	init_osc();			// initialise oscillator
	init_timers();		// initialise timers
	init_usart();		// initialise UART
	init_interrupts();	// initialise interrupts and set priorities

	// setup for SPI
	SPIC.CTRL = 0x52;	// enable SPI, MSB first, master mode, mode 0, SCK=perclk/64 (0.5MHz, CLK2x=0)

	// init USB
	SPI_RWreg(R17_PINCTL | WRITE, 0x1A);	// set full duplex, interrupt level, GPX on SOF
	SPI_RWreg(R15_USBCTL | WRITE, 0x20);	// reset
	SPI_RWreg(R15_USBCTL | WRITE, 0x00);	// end reset

	//wait for stable oscillator
	while((SPI_RWreg(R13_USBIRQ, 0)&0x01) == 0);	// wait for OSCOK flag
	
	SPI_RWreg(R12_EPIEN | WRITE, 0x30);	// interrupt on setup data, EP3 ***UPDATE IN BUS RESET TOO***
	SPI_RWreg(R14_USBIEN | WRITE, 0x88);// interrupt on USB bus reset

	SPI_RWreg(R11_EPIRQ | WRITE, 0xFF);	// clear interrupt flags
	SPI_RWreg(R13_USBIRQ | WRITE, 0xFF);// clear interrupt flags

	tick = 0;
	USB_configval = 0;	// start unconfigured
	USB_suspended = 0;	// assume not suspended on start
	USB_ep3stall = 0;	// EP3 not stalled at start (only bit 0 valid)
	sei();	// enable AVR interrupts

	SPI_RWreg(R16_CPUCTL | WRITE, 0x01);	// enable MAX3420 interrupts 
	SPI_RWreg(R15_USBCTL | WRITE, 0x08);	// connect to USB (bus powered so leave VBGATE)

	// *** MAIN LOOP ***
	while (1) {

		if (flg & 0x01) {	// regular events
			//u08 tmp;
			//tmp = SPI_RWreg(R20_IOPINS, 0);	//read buttons
			//SPI_RWreg(R20_IOPINS | WRITE, tmp>>4);	// write LEDs
			if ((tick&0x001F) == 0) {	// regular 512ms tick
				PORTH.OUTTGL = 0x08;	// LED toggle ******
				TXbuf_len = sprintf((char *)TXbuf, "E%02X/%02X(%02X) U%02X/%02X(%02X)\r\n", 
					SPI_RWreg(R11_EPIRQ, 0), SPI_RWreg(R12_EPIEN, 0), SPI_RWreg(R11_EPIRQ, 0)&SPI_RWreg(R12_EPIEN, 0),
					SPI_RWreg(R13_USBIRQ, 0), SPI_RWreg(R14_USBIEN, 0), SPI_RWreg(R13_USBIRQ, 0)&SPI_RWreg(R14_USBIEN, 0));	// create readable message*****
				dma_txbuf(&DMA.CH0, &USARTD0, DMA_CH_TRIGSRC_USARTD0_DRE_gc, (u08 *)TXbuf, TXbuf_len);	// queue for transmission*****
			}
			if ((tick&0x0007) == 3) {	// regular 128ms tick, offset from other tick
				if (SPI_RWreg(R11_EPIRQ, 0)&0x04) {	// OUT1DAVIRQ (EP1 data available)
					u08 dbuf[8], bytes;
					bytes = SPI_RWreg(R6_EP1OUTBC, 0);			// get no. bytes available
					USB_readFIFO(R1_EP1OUTFIFO, dbuf, bytes);	// read data from FIFO
					SPI_RWreg(R11_EPIRQ | WRITE, 0x04);	// clear OUT1DAVIRQ interrupt flag
					// do something with it?
					PORTH.OUTTGL = 0x01;	// LED toggle ******
				}
				if (SPI_RWreg(R11_EPIRQ, 0)&0x10) {	// IN3BAVIRQ (EP3 buffer empty)
					u08 keypkt[8];
					USB_create_key_pkt(keypkt);					// create a packet from the key inputs
					USB_writeFIFO(R3_EP3INFIFO, keypkt, 8);		// write string to FIFO
					SPI_RWreg(R8_EP3INBC | WRITE, 8);	// set no. bytes (clears IN3BAVIRQ too)
					PORTH.OUTTGL = 0x02;	// LED toggle ******
				}
			}
			flg &= ~0x01;	//clear the flag
		}
/*
		if (flg & 0x02) {	//send a status update message?
				flg &= ~0x02;	//clear flag
		}*/

	}
}
void USB_create_key_pkt(u08 *pkt) {
	u08 i, keys;
	//keys = (~(SPI_RWreg(R20_IOPINS, 0)>>4))&0x0F;	//read buttons
	keys = 0x00; //***TEST****
	for (i=0;i<8;i++)
		pkt[i] = 0;	// empty packet
	if (keys&0x01) pkt[2] = 4;
	if (keys&0x02) pkt[3] = 5;
	if (keys&0x04) pkt[4] = 6;
	if (keys&0x08) pkt[5] = 7;

}

// ********** INTERRUPT FUNCTIONS **********

// interrupt from pin B0 falling edge - INT
ISR(PORTB_INT0_vect) {
	u08 epirq, usbirq;
	epirq = SPI_RWreg(R11_EPIRQ, 0);	//read EPIRQ
	usbirq = SPI_RWreg(R13_USBIRQ, 0);	//read USBIRQ
	TXbuf_len = sprintf((char *)TXbuf, "E%02X(%02X) U%02X(%02X)\r\n", epirq, usbirq, SPI_RWreg(R12_EPIEN, 0)&epirq, SPI_RWreg(R14_USBIEN, 0)&usbirq);	// create readable message*****
	dma_txbuf(&DMA.CH0, &USARTD0, DMA_CH_TRIGSRC_USARTD0_DRE_gc, (u08 *)TXbuf, TXbuf_len);	// queue for transmission*****

// *** DATA PROCESSING ***
	if (epirq & 0x20) {	// SUDAVIRQ, setup data
		SPI_RWreg(R11_EPIRQ | WRITE, 0x20);	// clear SUDAVIRQ interrupt flag
		USB_setup_data();
	}
// *** SUSPEND/RESUME ***
	if ((USB_configval) && (usbirq&0x10)) {	// configured and SUSPIRQ indicates suspended bus
		SPI_RWreg(R14_USBIEN | WRITE, SPI_RWreg(R14_USBIEN, 0)&~0x10);	// clear SUSPIE to prevent continuous interrupts
		SPI_RWreg(R13_USBIRQ | WRITE, 0x14);							// clear SUSPIRQ and BUSACTIRQ interrupt flags
		SPI_RWreg(R14_USBIEN | WRITE, SPI_RWreg(R14_USBIEN, 0)|0x04);	// enable BUSACTIE interrupt
		USB_suspended = 1;
		// disable backlights, power things down here ***
		PORTH.OUTSET = 0x04;	// LED off ******
	}
	if ((USB_suspended)&&(usbirq&0x04)) {	// suspended, but bus activity detected
		SPI_RWreg(R14_USBIEN | WRITE, SPI_RWreg(R14_USBIEN, 0)&~0x04);	// clear BUSACTIE to prevent continuous interrupts
		SPI_RWreg(R13_USBIRQ | WRITE, 0x04);							// clear BUSACTIRQ interrupt flag
		SPI_RWreg(R14_USBIEN | WRITE, SPI_RWreg(R14_USBIEN, 0)|0x10);	// enable SUSPIE interrupt
		USB_suspended = 0;
		// re-enable backlights, power things back up here ***
		PORTH.OUTCLR = 0x04;	// LED on ******
	}
// *** BUS RESET ***
	if (usbirq & 0x08) {	// URESIRQ, bus reset interrupt
		SPI_RWreg(R13_USBIRQ | WRITE, 0x08);	// clear URESIRQ interrupt flag
	}
	if (usbirq & 0x80) {	// URESDNIRQ, bus reset interrupt done
		SPI_RWreg(R13_USBIRQ | WRITE, 0x80);	// clear URESDNIRQ interrupt flag
		USB_suspended = 0;
		// re-enable interupts (disabled on bus reset)
		//SPI_RWreg(R12_EPIEN | WRITE, 0x30);	// interrupt on setup data, EP3 ****
		SPI_RWreg(R12_EPIEN | WRITE, 0x20);	// interrupt on setup data only (TEST)
		SPI_RWreg(R14_USBIEN | WRITE, 0x88);// interrupt on USB bus reset
	}
	//flg |= 0x02;
}

ISR(TCD1_OVF_vect) {
	tick++;
	flg |= 0x01;
}

// ********** INIT FUNCTIONS **********

void init_osc(void) {	// initialise oscillator
//CLOCK SETUP
	// system oscillator
	OSC.CTRL = 0x03;					// enable 2MHz and 32MHz clocks
	while ((OSC.STATUS&0x02) == 0) {}	// wait for 32MHz oscillator startup
	CCP = 0xD8;							// enable change of protected register
	CLK.CTRL = 0x01;					// select 32MHz clock
}


void init_interrupts(void) {	// initialise interrupts and set priorities
//INTERRUPTS
	PMIC.CTRL |= 0x81;		// enable low level interrupts with round-robin scheduling
	PMIC.CTRL |= 0x02;		// enable medium level interrupts

	PORTB.INTCTRL |= 0x01;	// low level interrupt for port interrupt 0
	PORTB.INT0MASK = 0x01;	// enable interrupt on B0

	TCD1.INTCTRLA |= 0x01;		// set timer D1 overflow interrupt to low priority
}

void init_timers(void) {	// initialise timers
//TIMER SETUP

	// timer D1	- 16 ms tick for misc timing
	TCD1.CTRLA = 0x04;			// prescaler /8 to give 4MHz clock for counter
	TCD1.PER = 64000;			// set top value to 16 ms
}


